﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace Ttifa.Infrastructure.Utils
{
    /// <summary>
    /// 验证码生成类
    /// </summary>
    public class VerifyCode
    {
        private int _length = 4;                        //验证码长度
        private int _fontSize = 18;              //字体最大尺寸
        private int _border = 0;                      //边框，0时没有边框
        private Color _backgroundColor = Color.AliceBlue;      //背景色
        private int _rateNumber = 10;              //验证码中的数字出现机率,越大出现的数字机率越大
        private string _randomChars;              //随机生成的验证码
        private int _randomAngle = 66;              //随机码的旋转角度
        private static string[] FontFamilys = new string[] { "Verdana", "Arial", "宋体", "微软雅黑", "黑体" };        //字体组
        private int _chaosNumber = 0;                //噪点数量  ,0 时不用
        private Random random = new Random();    //随机种子
        private CodeStyle _style = CodeStyle.Common; //校验码类型
        private static Color[] _fontColorArr = new Color[] { Color.Blue, Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.OrangeRed,
            Color.Brown, Color.Black };  //颜色数组
        private static string[] _chineseArr = new string[] { "洞若观火", "对簿公堂", "百花争妍", "耳濡目染", "耳熟能详", "耳提面命", "罚不当罪",
            "翻云覆雨", "繁文缛节", "方兴未艾", "沸反盈天", "焚膏继晷", "粉墨登场", "风驰电掣", "奉为圭臬", "否极泰来", "浮光掠影",
            "高谈阔论", "隔靴搔痒", "各行其是", "耿耿于怀", "功败垂成", "狗尾续貂", "孤注一掷", "瓜田李下", "刮目相看", "挂一漏万",
            "管窥蠡测", "光天化日", "鬼斧神工", "含英咀华", "汗牛充栋", "皓首穷经", "厚积薄发", "虎视眈眈", "怙恶不悛", "涣然冰释",
            "荒诞不经", "黄粱一梦", "讳莫如深", "火中取栗", "祸起萧墙", "积重难返", "集腋成裘", "计日程功", "济济一堂", "间不容发",
            "见仁见智", "见贤思齐", "矫揉造作", "矫枉过正", "金科玉律", "噤若寒蝉", "敬谢不敏", "久假不归", "开卷有益", "恪遵功令",
            "空穴来风", "苦心孤诣", "狼奔豕突", "良莠不齐", "临渊羡鱼", "令人发指", "另眼相看", "门可罗雀", "门庭若市", "面目全非",
            "目无全牛", "沐猴而冠", "暮鼓晨钟", "南辕北辙", "南辕北辙", "泥沙俱下", "抛砖引玉", "蓬荜生辉", "披肝沥胆", "披沙拣金",
            "萍水相逢", "期期艾艾", "杞人忧天", "前事不忘", "黔驴技穷", "罄竹难书", "求全责备", "曲突徙薪", "人心不古", "忍俊不禁",
            "如汤沃雪", "如坐春风", "茹毛饮血", "塞翁失马", "三人成虎", "三足鼎立", "色厉内荏", "闪烁其词", "身体力行", "尸位素餐",
            "师心自用", "石破天惊", "时乖运蹇", "拾人牙慧", "始作俑者", "首鼠两端", "受宠若惊", "数典忘祖", "水落石出", "硕大无朋",
            "硕果仅存", "司空见惯", "夙兴夜寐", "素昧平生", "叹为观止", "韬光养晦", "醍醐灌顶", "条分缕析", "痛心疾首", "万马齐喑",
            "妄自菲薄", "欣喜若狂", "心悦诚服", "喜笑颜开", "喜怒无常", "喜出望外", "心花怒放", "喜眉笑眼", "喜不自胜", "谈笑风生",
            "手舞足蹈", "受宠若惊", "人莫予毒", "亲痛仇快", "乐极生悲", "眉开眼笑", "哭丧着脸", "酒酣耳热", "惊喜欲狂", "近悦远来",
            "皆大欢喜", "欢欣鼓舞", "含笑九泉", "欢天喜地", "欢呼雀跃", "抚掌大笑", "怫然不悦", "得意忘形", "大喜过望", "冁然而笑",
            "眉欢眼笑", "眉花眼笑", "眉飞眼笑", "惊喜交集", "形槁心灰", "息怒停瞋", "宜嗔宜喜", "旭日初升", "鲜蹦活跳", "死样活气",
            "浓眉大眼", "艴然不悦", "负气含灵", "半死半生", "转嗔为喜", "心平气和", "喜怒无常", "生龙活虎", "回嗔作喜", "拂袖而去",
            "肝胆欲碎", "愤愤不平", "勃然变色", "别开生面", "活蹦乱跳", "不死不活" };

        private string _formula;    //公式变量
        private List<Formula> Formulas = new List<Formula>();

        public enum CodeStyle
        {
            Simple,
            Common,
            Formula,
            Chinese
        }

        /// <summary>
        /// 验证码
        /// </summary>
        public string VCode
        {
            get { return _randomChars; }
        }

        /// <summary>
        /// 边框粗细
        /// </summary>
        public int Border
        {
            set { _border = value; }
        }

        /// <summary>
        /// 验证码长度
        /// </summary>
        public int Length
        {
            get { return _length; }
            set { _length = value; }
        }

        /// <summary>
        /// 验证码类型
        /// </summary>
        public CodeStyle Style
        {
            get { return _style; }
            set { _style = value; }
        }

        private Formula Formula_Create()
        {
            var result = new Formula() { formula = "1 + 1 = ", result = "2" };
            //先随机加减乘除
            var n = random.Next(4);
            int a, b, c;

            //加
            if (n == 0)
            {
                a = random.Next(50);
                b = random.Next(50);
                c = a + b;

                result.formula = string.Format("{0} + {1} = ", a, b);
                result.result = c.ToString();
            }

            //减
            if (n == 1)
            {
                a = random.Next(50);
                b = random.Next(50);
                c = a - b;

                result.formula = string.Format("{0} - {1} = ", a, b);
                result.result = c.ToString();
            }

            //乘
            if (n == 2)
            {
                a = random.Next(15);
                b = random.Next(10);
                c = a * b;

                result.formula = string.Format("{0} × {1} = ", a, b);
                result.result = c.ToString();
            }

            //除
            if (n == 3)
            {
                a = random.Next(1, 15);
                b = random.Next(1, 10);
                c = a * b;

                result.formula = string.Format("{0} ÷ {1} = ", c, a);
                result.result = b.ToString();
            }

            return result;
        }

        private string Chinese_Char_Create()
        {
            var result = string.Empty;

            //for (int i = 1; i <= _length; i++)
            //{
            //    //汉字库随机取汉字
            //    //int n1 = rnd.Next(176, 200);
            //    //int n2 = rnd.Next(161, 254);
            //    //Byte b1 = Convert.ToByte(n1.ToString(), 10);
            //    //Byte b2 = Convert.ToByte(n2.ToString(), 10);
            //    //Encoding gb = Encoding.GetEncoding("gb2312");
            //    //Byte[] bytes = new Byte[] { b1, b2 };
            //    //result += gb.GetString(bytes);

            //    //简单汉字集合
            //    result += _chineseArr[rnd.Next(_chineseArr.Length)];
            //}

            result = _chineseArr[random.Next(_chineseArr.Length)];

            return result;
        }

        /// <summary>
        /// 生成随机验证码
        /// </summary>
        private void CreateCode()
        {
            switch (_style)
            {
                default:
                    char code;
                    for (int i = 0; i < _length; i++)
                    {
                        int rand = random.Next();
                        if (rand % _rateNumber == 0)
                            code = (char)('A' + (char)(rand % 26));
                        else
                            code = (char)('0' + (char)(rand % 10));

                        _randomChars += code.ToString();
                    }
                    //_randomAngle = 40;
                    break;
                case CodeStyle.Formula:
                    var temp = Formula_Create();
                    _randomChars = temp.result;
                    _formula = temp.formula;
                    _randomAngle = 5;
                    break;
                case CodeStyle.Chinese:
                    var temp1 = Chinese_Char_Create();
                    _randomChars = temp1;
                    break;
            }

        }

        ///  <summary>
        ///  背景噪点生成
        ///  </summary>
        private void CreateBackgroundChaos(Bitmap map, Graphics graph)
        {
            Pen blackPen = new Pen(Color.Azure, 0);
            for (int i = 0; i < map.Width * 2; i++)
            {
                int x = random.Next(map.Width);
                int y = random.Next(map.Height);
                graph.DrawRectangle(blackPen, x, y, 1, 1);
            }
        }

        ///  <summary>
        ///  前景色噪点
        ///  </summary>
        private void CreaetForeChaos(Bitmap map)
        {
            for (int i = 0; i < map.Width * _chaosNumber; i++)
            {
                int x = random.Next(map.Width);
                int y = random.Next(map.Height);
                map.SetPixel(x, y, Color.FromArgb(random.Next(300)));
                //map.SetPixel(x, y, _fontColor);
            }
        }

        #region 产生波形滤镜效果

        private const double PI = 3.1415926535897932384626433832795;
        private const double PI2 = 6.283185307179586476925286766559;
        //private const double PI = 0;
        //private const double PI2 = 8;

        /// <summary>
        /// 正弦曲线Wave扭曲图片
        /// </summary>
        /// <param name="srcBmp">图片路径</param>
        /// <param name="bXDir">如果扭曲则选择为True</param>
        /// <param name="nMultValue">波形的幅度倍数，越大扭曲的程度越高，一般为3</param>
        /// <param name="dPhase">波形的起始相位，取值区间[0-2*PI)</param>
        /// <returns></returns>
        public System.Drawing.Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase)
        {
            System.Drawing.Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height);

            // 将位图背景填充为白色
            System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(destBmp);
            graph.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, destBmp.Width, destBmp.Height);
            graph.Dispose();

            double dBaseAxisLen = bXDir ? (double)destBmp.Height : (double)destBmp.Width;

            for (int i = 0; i < destBmp.Width; i++)
            {
                for (int j = 0; j < destBmp.Height; j++)
                {
                    double dx = 0;
                    dx = bXDir ? (PI2 * (double)j) / dBaseAxisLen : (PI2 * (double)i) / dBaseAxisLen;
                    dx += dPhase;
                    double dy = Math.Sin(dx);

                    // 取得当前点的颜色
                    int nOldX = 0, nOldY = 0;
                    nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
                    nOldY = bXDir ? j : j + (int)(dy * dMultValue);

                    System.Drawing.Color color = srcBmp.GetPixel(i, j);
                    if (nOldX >= 0 && nOldX < destBmp.Width
                     && nOldY >= 0 && nOldY < destBmp.Height)
                    {
                        destBmp.SetPixel(nOldX, nOldY, color);
                    }
                }
            }

            return destBmp;
        }

        #endregion

        ///  <summary>
        ///  创建随机码图片
        ///  </summary>
        ///  <param  name="context"></param>
        public void CreateImage(HttpContextBase context)
        {
            CreateCode();          //创建验证码
            Bitmap map;
            switch (_style)
            {
                case CodeStyle.Chinese:
                    map = new Bitmap((int)(_randomChars.Length * 26), 26);
                    break;
                case CodeStyle.Formula:
                    map = new Bitmap((int)(_formula.Length * 16), 26);
                    break;
                default:
                    map = new Bitmap((int)(_randomChars.Length * 16), 26);
                    break;
            }
            //创建图片背景
            Graphics graph = Graphics.FromImage(map);
            graph.FillRectangle(new SolidBrush(Color.Black), 0, 0, map.Width + 1, map.Height + 1);          //填充一个有背景的矩形

            //if  (_border  >  0)  //画一个边框
            //{
            //  graph.DrawRectangle(new  Pen(Color.Black,  0),  0,  0,  map.Width  -  _border,  map.Height  -  _border);
            //}        
            graph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;        //模式
            graph.Clear(_backgroundColor);              //清除画面，填充背景           
            Point dot = new Point(12, 12);
            CreateBackgroundChaos(map, graph);              //背景噪点生成
            CreaetForeChaos(map);              //前景色噪点
            //文字距中
            StringFormat format = new StringFormat(StringFormatFlags.NoClip);
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;
            //验证码旋转，防止机器识别
            char[] chars;
            switch (_style)
            {
                case CodeStyle.Formula:
                    chars = _formula.ToCharArray();
                    break;
                default:
                    chars = _randomChars.ToCharArray();
                    break;
            }
            //拆散字符串成单字符数组

            for (int i = 0; i < chars.Length; i++)
            {
                //SolidBrush brush = new SolidBrush(_fontColor);    //画笔
                SolidBrush brush = new SolidBrush(_fontColorArr[random.Next(_fontColorArr.Count())]);
                Font fontstyle = new Font(FontFamilys[random.Next(FontFamilys.Count())], random.Next(_fontSize - 3, _fontSize), FontStyle.Regular);            //字体样式
                float angle = random.Next(-_randomAngle, _randomAngle);            //转动的度数
                graph.TranslateTransform(dot.X, dot.Y);          //移动光标到指定位置
                graph.RotateTransform(angle);
                graph.DrawString(chars[i].ToString(), fontstyle, brush, -2, 2, format);
                graph.RotateTransform(-angle);                    //转回去
                //移动光标到指定位置
                if (_style == CodeStyle.Chinese)
                {
                    graph.TranslateTransform(12, -dot.Y);
                }
                else
                {
                    graph.TranslateTransform(2, -dot.Y);
                }
            }
            if (_style != CodeStyle.Simple)
                map = TwistImage(map, true, 3, 3);

            //生成图片
            MemoryStream ms = new MemoryStream();
            map.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
            context.Response.ClearContent();
            context.Response.ContentType = "image/png";
            context.Response.BinaryWrite(ms.ToArray());

            graph.Dispose();
            map.Dispose();
        }

        /// <summary>
        /// 初始化验证码
        /// </summary>
        public VerifyCode()
        {
        }

        public VerifyCode(CodeStyle style)
        {
            _style = style;
        }
    }

    class Formula
    {
        public string formula { get; set; }
        public string result { get; set; }
    }
}
